﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;

namespace Chaos.network
{
    class gameserver
    {
        private Thread m_thread;
        private List<tcpconnection> m_connections;
        public static int Port { get { return 48927; } }
        public bool Running { get; set; }

        public gameserver()
		{
            m_thread = new Thread(new ThreadStart(threadmain));
            m_thread.IsBackground = true;
            m_thread.Name = "gameserver_thread";
            m_connections = new List<tcpconnection>();
            Running = false;
		}

        public tcpconnection findconnectionbyuser(String in_user)
        {
            for (int i = 0; i < m_connections.Count; i++)
            {
                if (m_connections[i].User == in_user)
                {
                    return m_connections[i];
                }
            }
            return null;
        }

        public void start()
        {
            if (!Running)
            {
                Running = true;
                m_thread = new Thread(new ThreadStart(threadmain));
                m_thread.IsBackground = true;
                m_thread.Name = "gameserver_thread";
                m_thread.Start();
            }
        }

        public void abort()
        {
            if (Running)
            {
                Running = false;
                //m_thread.Abort();
                m_thread.Join();
            }
        }

        public void shutdown()
        {
            Running = false;
            Thread.Sleep(100);
            abort();
            lock (m_connections)
            {
                List<tcpconnection> tmp = new List<tcpconnection>(m_connections);
                foreach (tcpconnection tcpconnection in tmp)
                {
                    tcpconnection.abort();
                }
            }
        }

        public void BroadcastMessage(messages.message message)
        {
            lock (m_connections)
            {
                foreach (tcpconnection tcpconnection in m_connections)
                {
                    tcpconnection.sendmessage(message);
                }
            }
        }

        private void threadmain()
        {
            try
            {
                TcpListener listener = new TcpListener(IPAddress.Any, Port);
                listener.Start();

                while (Running)
                {
                    try
                    {
                        if (listener.Pending())
                        {
                            TcpClient clientSocket = listener.AcceptTcpClient();
   
                            // see if this is the same tcpconnection reconnecting
                            /*
                            lock (m_connections)
                            {
                                foreach (tcpconnection c in m_connections)
                                {
                                    if ((c.TcpClient.Client.RemoteEndPoint as IPEndPoint).Address.ToString() == connectingClientAddress)
                                    {
                                        Console.WriteLine("Reconnected tcpconnection");
                                        c.Abort();  
                                        break;
                                    }
                                }
                            }
                            */

                            Console.WriteLine("Client connected!");
                            IPEndPoint ipendpoint = clientSocket.Client.RemoteEndPoint as IPEndPoint;
                            tcpconnection conn = new tcpconnection(clientSocket, ipendpoint);
                            conn.OnNetworkConnected += new tcpconnection.OnNetworkConnectedDelegate(conn_OnNetworkConnected);
                            conn.OnNetworkDisConnected += new tcpconnection.OnNetworkDisConnectedDelegate(conn_OnNetworkDisConnected);
                            conn.OnNetworkData += new tcpconnection.OnNetworkDataDelegate(conn_OnNetworkData);
                            conn.start();
                        }
                        else
                        {
                            Thread.Sleep(25);
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.ToString());
                        Thread.Sleep(100);
                    }
                }

                listener.Stop();
            }
            catch (ThreadAbortException abortException)
            {
                Console.WriteLine((string)abortException.ExceptionState);
            }
            catch (Exception)
            {
            }
        }

        void conn_OnNetworkData(object sender, tcpconnection.OnNetworkDataEvent e)
        {
            messages.message.context c = new messages.message.context();
            c.Connection = sender as connection;
            c.IPEndPoint = e.IPEndPoint;
            e.Message.process(c);
        }

        void conn_OnNetworkConnected(object sender, tcpconnection.OnNetworkConnectedEvent e)
        {
            AddClient(sender as tcpconnection); 
        }

        void conn_OnNetworkDisConnected(object sender, tcpconnection.OnNetworkDisConnectedEvent e)
        {
            RemoveClient(sender as tcpconnection);
        }

        public void RemoveClient(tcpconnection tcpconnection)
        {
            lock (m_connections)
            {
                m_connections.Remove(tcpconnection);
            }
            if (OnClientDisconnect != null)
            {
                OnClientDisconnect();
            }
        }

        public void AddClient(tcpconnection tcpconnection)
        {
            lock (m_connections)
            {
                m_connections.Add(tcpconnection);
            }
            if (OnClientConnect != null)
            {
                OnClientConnect();
            }

            messages.request_user_info msg = new messages.request_user_info();
            tcpconnection.sendmessage(msg);
        }

        public delegate void OnClientConnectDelegate();
        public static event OnClientConnectDelegate OnClientConnect;

        public delegate void OnClientDisconnectDelegate();
        public static event OnClientDisconnectDelegate OnClientDisconnect;
    }
}
